2

第一次翻译,可能有很多地方不恰当,欢迎指正。

原文地址:http://javascriptweblog.wordpress.com/2010/09/27/the-secret-life-of-javascript-primitives/

你可能不知道,在javascript中,在使用string, number, 布尔类型这些原始值时,都会遇到意想不到的问题。阅读下文,来揭晓里面的秘密。

基础介绍

对象就是各个属性的集合,属性可以是对一个对象的引用或是一个原始值,原始值是一个值,他们没有属性。

在 JS 里的五类原始类型是:undefined, null, boolean, string 和 number."其余"的类型都是对象.其中 boolean, string 和 number 原始类型可以被对应的对象类型包裹.这些对象都应该是 Boolean, String 和 Number 类的实例。

typeof true; //"boolean"
typeof Boolean(true); //"boolean"
typeof new Boolean(true); //"object"
typeof (new Boolean(true)).valueOf(); //"boolean"

typeof "abc"; //"string"
typeof String("abc"); //"string"
typeof new String("abc"); //"object"
typeof (new String("abc")).valueOf(); //"string"

typeof 123; //"number"
typeof Number(123); //"number"
typeof new Number(123); //"object"
typeof (new Number(123)).valueOf(); //"number"

如果原始值没有属性,那为什么"abc".length能返回值?

因为javascript在原始值与对象之间很轻松的互换类型。在这种情况下,为了得到length属性,字符串的value值就被强制转换为字符串对象。String对象仅仅是被瞬间转换,然后会被当着垃圾回收处理掉。对于这种现象,我们先避开这个难解的谜团,接下来慢慢分析它。

String.prototype.returnMe= function() {
    return this;
}

var a = "abc";
var b = a.returnMe(); 

a; //"abc"
typeof a; //"string" (still a primitive)
b; //"abc"
typeof b; //"object"

但是你会发现,只要这个对象还存在,这个对象不会被当着垃圾回收来处理。

在strict模式下,这种现象不会出现。

这里有一个典型的实例来解释这个对象没有被当着垃圾回收来处理掉。

Number.prototype.toString = function() {
    return typeof this;
}

(123).toString(); //"object"

也就是说原始值是有属性的(包括方法),这些属性是他们各自的原型所定义的。

这些对象能被强制转换为values吗?

是的,大多数情况下,对象仅仅是容器,value是他们包含的原始值,他们会按需的强制性转换为对应的value值。来看下面的实例:

//object coerced to primitive
var Twelve = new Number(12);
var fifteen = Twelve + 3;
fifteen; //15
typeof fifteen; //"number" (primitive)
typeof Twelve; //"object"; (still object)

//another object coerced to primitive
new String("hippo") + "potamus"; //"hippopotamus"

//object not coerced (because 'typeof' operator can work with objects)
typeof new String("hippo") + "potamus"; //"objectpotamus"

奇怪的是boolean 对象不会被轻易的转换,除了遇到null 与undefined, boolean 对象都会自动修正为true,试试这个:

if (new Boolean(false)) {
    alert("true???");
}

通常你可能想清楚的知道boolean 对象的value值,下面的做法能明确的断定value值是true,还是false

var a = "";
new Boolean(a).valueOf(); //false

但是,这样做可能更容易些:

var a = Boolean("");
a; //false

甚至是这样:

var a = "";
!!a; //false

强制转换允许我们对一个原始值赋值吗?

答案是no

var primitive = "september";
primitive.vowels = 3;

primitive.vowels; //undefined;

如果javascript探测到试图对一个原始值赋值,事实上它会强制把这个原始值转换为对象,但是,正如上面的实例,这个新对象是没有指针的,会立刻被回收处理掉。

这里有个伪代码实例来解释这种现象:

var primitive = "september";
primitive.vowels = 3;
//new object created to set property
(new String("september")).vowels = 3;

primitive.vowels;
//another new object created to retrieve property
(new String("september")).vowels; //undefined

正如你看到的,这种写法不仅无效,而且是相当的浪费。

结论

最后证明相对于对原始值赋值,给对象赋值是它的一个唯一优势。但是在实践中,这种做法也是可疑的。Strings, booleans 与numbers有着特定的,定义好的用途。重新定义他们恰恰让人难以理解。而且,原始值是不可变的,你不可能通过改变他们的属性值来修改他们。

var me = new String("Angus");
me.length = 2; //(error in strict mode)
me.length; //5 (not 2 - thanks @joseanpg)
me.valueOf(); "Angus"

而且,我认为对原始值深刻的理解,以及当使用他们的时候知道具体发生了什么是深入理解这门语言迈出重要的一步。


小渝人儿
1.1k 声望850 粉丝

前端工程师